package Engine.Model
{
	import Engine.Manager.EngineManager;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.utils.Timer;
	import flash.utils.getQualifiedClassName;
	
	import library.SocketData.SocketData;
	
	public class GameSocket extends Socket
	{
		
		private static const MIN_PACK_HEAD_SIZE:int   = 6;
		
		private var _connectedFun:Function;
		private var _disConnectedFun:Function;
		
		private var recvBytes:ByteArray = new ByteArray();
		private var _socketName:String = "";
		
		public function GameSocket(sname:String)
		{
			this._socketName = sname;
			this.recvBytes.clear();
			this.recvBytes.endian = Endian.LITTLE_ENDIAN;
			this.configEventListenfun();
		}
		
		private function configEventListenfun():void
		{
			this.addEventListener(IOErrorEvent.IO_ERROR,ioErrHandler);
			this.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrHandler);
			this.addEventListener(Event.CONNECT,connectHandler);
			this.addEventListener(Event.CLOSE,closesockethandler);
			this.addEventListener(ProgressEvent.SOCKET_DATA,receiveMessageData);
		}
		
		private function ioErrHandler(event:IOErrorEvent ):void
		{
			EngineManager.logPrint.printLog("ioErrHandler",event.toString());
		}
		
		private function securityErrHandler(event:SecurityErrorEvent ):void
		{
			EngineManager.logPrint.printLog("securityErrHandler",event.toString());
		}
		
		private function connectHandler(event:Event):void
		{
			this._connectedFun();
		}
		
		private function closesockethandler(event:Event):void
		{
			EngineManager.logPrint.printLog("closesockethandler");
			this._disConnectedFun();
			this.recvBytes.clear();
		}
		
		private function receiveMessageData(event:ProgressEvent):void
		{
			if(!this.bytesAvailable)
				return;
			if(this.recvBytes.length>0)
			{
				this.recvBytes.position = this.recvBytes.length;
				this.readBytes(this.recvBytes);
			}
			else
			{
				this.readBytes(this.recvBytes);
			}
			if(this.recvBytes.length < MIN_PACK_HEAD_SIZE)
				return;
			var packsize:int=0;
			var packFlag:int=0;
			var packtype:int=0;
			var packID:int = 0;
			var packData:ByteArray = new ByteArray();
			packData.endian = Endian.LITTLE_ENDIAN;
			while(this.recvBytes.length > 0)
			{
				if(this.recvBytes.length < MIN_PACK_HEAD_SIZE)
					return;
				this.recvBytes.position = 0;
				packsize = this.recvBytes.readShort();
				if(packsize < MIN_PACK_HEAD_SIZE)
				{
					//this.recvBytes.clear();
					EngineManager.logPrint.printLog("receive message length error",MIN_PACK_HEAD_SIZE);
					return;
				}
				
				if(this.recvBytes.length < packsize)
					return;
				packFlag = this.recvBytes.readByte();
				packtype = this.recvBytes.readByte();
				packID = this.recvBytes.readShort();
				packData.clear();
				if(packsize != MIN_PACK_HEAD_SIZE)
					packData.writeBytes(this.recvBytes,MIN_PACK_HEAD_SIZE,packsize - MIN_PACK_HEAD_SIZE);
				
				this.handleMessage(packFlag,packtype,packID,packData);
				
				if(this.recvBytes.length > packsize)
				{
					var tmpBytes:ByteArray = new ByteArray;
					tmpBytes.endian = Endian.LITTLE_ENDIAN;
					tmpBytes.writeBytes(this.recvBytes,packsize);
					this.recvBytes = tmpBytes;
					this.recvBytes.position = 0;
				}
				else
				{
					this.recvBytes.clear();
					return;
				}
			}
		}
		
		
		private function handleMessage(packFlag:int,packType:int,packID:int,packData:ByteArray):void
		{
			EngineManager.logPrint.printLog("Receive Message packFlag:"+String(packFlag)+" packType:"+String(packType)+" packID:"+String(packID)+" packLength:"+packData.length.toString());
			if(LogPrint.DEBUG)
				EngineManager.eventMgr.throwEvent(packID,new SocketData(packData));
			else{
				try{
					EngineManager.eventMgr.throwEvent(packID,new SocketData(packData));
				}
				catch(e:Error)
				{
					EngineManager.logPrint.printLog("Handle Message error Message ID:",packID);
					EngineManager.logPrint.printLog(e.getStackTrace());
				}
			}
		}
		
		public function sendMessageData(msgID:int,msgData:*,msgType:int = 1,msgFlag:int = 0):void
		{
			if(!this.connected)
				return;
			
			var sendBytes:SocketData = new SocketData();
			sendBytes.endian = Endian.LITTLE_ENDIAN;
			
			switch(getQualifiedClassName(msgData))
			{
				case "int":
					sendBytes.writeInt16(msgData);
					break;
				case "Number":
					sendBytes.writeFloat(msgData);
					break;
				case "String":
					sendBytes.writeString(msgData);
					break;
				case "library.SocketData::SocketData":
					sendBytes = msgData as SocketData;
					break;
				case "flash.utils::ByteArray":
					sendBytes = new SocketData(msgData);
					break;
				default:
					EngineManager.logPrint.printLog("haven't send Type:"+msgID.toString()+" Value:" + String(msgData)+"DateType not support",getQualifiedClassName(msgData));
					return; 
			}
			
			var length:int = MIN_PACK_HEAD_SIZE + sendBytes.length;
			var data:ByteArray = new ByteArray();
			data.endian = Endian.LITTLE_ENDIAN ;
			data.writeShort(length);
			data.writeByte(msgFlag);
			data.writeByte(msgType);
			data.writeShort(msgID);
			data.writeBytes(sendBytes, 0, sendBytes.length);
			
			data.position = 0;
			
			var packSize:int = data.readShort();
			if(packSize != data.length)
			{
				EngineManager.logPrint.printLog("invalid send type:",msgID);
				return;
			}
			
			data.position = 0;
			
			this.writeBytes(data,0,data.length);
			this.flush();
			
			EngineManager.logPrint.printLog("Send Message msgID:",msgID,"length:",data.length,"msgType:",msgType);
			
		}
		
		public function connectSocket(ip:String,port:int):void
		{
			if(this.connected)
				return;
			EngineManager.logPrint.printLog("Connect Game IP:",ip,"Port:",port);
			this.connect(ip,port);
		}
		
		public function setConnectFun(fun:Function):void
		{
			this._connectedFun = fun;
		}
		
		public function setDisConnectFun(fun:Function):void
		{
			this._disConnectedFun = fun;
		}
		
		
		public function disconnected():void
		{
			if(!this.connected)
				return;
			this.close();
		}
	}
}